package pagemaker;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

import javax.swing.ImageIcon;

// import ut.Log;
/**
 * An implementation of IPDFPage that uses the muPDF application to generate
 * images of pages.
 *
 * @author Thibault Langlois
 *
 */
public class MuPDFPageMaker implements IPDFPageMaker {

    protected File pdfFile;
    private int nbPages;
    private static String program = "/usr/bin/pdfdraw";
    private static String cacheDirectory = "";

    /**
     * Constructor.
     *
     * @param pdfFile
     * @throws IOException
     * @throws ExternalProgramException
     * @requires pdfFile.exists() && pdfFile.canRead() && pdfFile contains a
     * document in PDF format.
     */
    public MuPDFPageMaker(File pdfFile) throws IOException, ExternalProgramException {
        this.pdfFile = pdfFile;
        makeAllPages(20);
    }

    public MuPDFPageMaker(File pdfFile, int nbPages) throws IOException, ExternalProgramException {
        this.pdfFile = pdfFile;
        boolean recompute = false;
        for (int page = 1; page <= nbPages; page++) {
            if (!this.pageImageInCache(page, 20)) {
                recompute = true;
                continue;
            }
        }
        // if some thumbnail is missing recompute pages.
        if (recompute) {
            makeAllPages(20);
        } else {
            this.nbPages = nbPages;
        }
    }

    /* (non-Javadoc)
     * @see domain.IPDFPage#getNumberOfPages()
     */
    public int getNumberOfPages() {
        return this.nbPages;
    }

    /**
     * Sets the executable that should be used to run muPDF. If not set the
     * default value is "/usr/bin/pdfdraw".
     *
     * @param p
     */
    public static void setProgram(String p) {
        program = p;
    }

    public static void setCacheDirectory(String p) {
        cacheDirectory = p;
    }

    /* (non-Javadoc)
     * @see domain.IPDFPage#getPage(int, int)
     */
    @Override
    public ImageIcon getPage(int pageNumber, int dpi) throws IOException, ExternalProgramException {
        if (pageImageInCache(pageNumber, dpi)) {
            return loadImage(pageNumber, dpi);
        } else {
            makePages(dpi, pageNumber);
            return loadImage(pageNumber, dpi);
        }
    }

    private ImageIcon loadImage(int pageNumber, int dpi) {
        File f = pageFile(pageNumber, dpi);
        ImageIcon image = new ImageIcon(f.toString());
        return image;
    }

    private void makeAllPages(int dpi) throws IOException, ExternalProgramException {
        // Log.prt("Let's run : " + program + " -r " + dpi 
        //                    + " -o " + cacheDirectory + getPageNamePrefix(dpi) + "-" + "%d.png " + pdfFile.getAbsolutePath());
        Process p = Runtime.getRuntime()
                .exec(program + " -r " + dpi
                + " -o " + cacheDirectory + getPageNamePrefix(dpi) + "-" + "%d.png " + pdfFile.getAbsolutePath());
        try {
            p.waitFor();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // Log.prt("Done (" + p.exitValue() + ").");
        if (p.exitValue() != 0) {
            System.err.println("File " + pdfFile.getAbsolutePath() + " exists ? -> " + pdfFile.exists());
            throw new ExternalProgramException("Error generating pages. exit code = " + p.exitValue());
        }
        Set<File> pageFiles = listPagesFilenames(dpi);
        this.nbPages = pageFiles.size();
    }

    private void makePages(int dpi, int fromPage) throws IOException, ExternalProgramException {
        // Log.prt("Let's run : " + program + " -r " + dpi 
        //                   + " -o " + cacheDirectory + getPageNamePrefix(dpi) + "-" + "%d.png " + pdfFile.getAbsolutePath());
        Process p = Runtime.getRuntime()
                .exec(program + " -r " + dpi
                + " -o " + cacheDirectory + getPageNamePrefix(dpi) + "-" + "%d.png "
                + pdfFile.getAbsolutePath() + " " + fromPage + "-" + (fromPage + 10));
        try {
            p.waitFor();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // Log.prt("Done (" + p.exitValue() + ").");
        if (p.exitValue() != 0) {
            throw new ExternalProgramException("Error generating pages. exit code = " + p.exitValue());
        }
        Set<File> pageFiles = listPagesFilenames(dpi);
        this.nbPages = pageFiles.size();
    }

    private Set<File> listPagesFilenames(int dpi) {
        // System.out.printf("in listPagesFilenames getPageNamePrefix : %s\n", getPageNamePrefix(dpi));
        File directory = new File(cacheDirectory);
        final String prefix = getPageNamePrefix(dpi);
        FilenameFilter myFilter = new FilenameFilter() {
            public boolean accept(File dir, String name) {
                if (name.startsWith(prefix) && name.endsWith("png")) {
                    return true;
                } else {
                    return false;
                }
            }
        };
        File[] files = directory.listFiles(myFilter);
        HashSet<File> theFiles = new HashSet<File>();
        for (File f : files) {
            theFiles.add(f);
        }
        return theFiles;
    }

    private String getPageNamePrefix(int dpi) {
        String theName = pdfFile.getName();
        int pos = theName.lastIndexOf(".");
        if (pos > 0) {
            theName = theName.substring(0, pos);
        }
        theName += "-" + dpi + "dpi";
        return theName;
    }

    private File pageFile(int page, int dpi) {
        return new File(cacheDirectory + this.getPageNamePrefix(dpi) + "-" + page + ".png");
    }

    private boolean pageImageInCache(int page, int dpi) {
        // Log.prt("in pageImageInCache: file is " + pageFile(page,dpi) + " is in cache : " + pageFile(page,dpi).exists());
        return pageFile(page, dpi).exists();
    }
}
